/*++


 Copyright (c) 2006 Microsoft Corporation.  All rights reserved.

 The use and distribution terms for this software are contained in the file
 named license.txt, which can be found in the root of this distribution.
 By using this software in any fashion, you are agreeing to be bound by the
 terms of this license.

 You must not remove this notice, or any other, from this software.


Module Name:

    tls.c

Abstract:

    Implementation of platform-specific Thread local storage functions.

--*/

#include "pal/palinternal.h"

#include <pthread.h>

#include "pal/dbgmsg.h"
#include "pal/thread.h"
#include "pal/misc.h"
#include "pal/debug.h"

#include <stddef.h>

SET_DEFAULT_DEBUG_CHANNEL(THREAD);

#if defined(USE_OPTIMIZEDTLSGETTER)

/*++
Function:
    TLSMakeOptimizedGetter

    Creates a platform-optimized version of TlsGetValue compiled
    for a particular index.

    Generates the hot part of PROCGetCurrentThreadObjectInternal
    as a chunk of highly optimized machine-specific code at runtime.

    Check the difference between PROCGetCurrentThreadObjectInternal and
    PROCGetCurrentThreadObjectSlow to see the C/C++ code that matches
    the code generated by this function.
--*/
PAL_POPTIMIZEDTLSGETTER
TLSMakeOptimizedGetter(
        IN DWORD dwTlsIndex)
{
    PAL_POPTIMIZEDTLSGETTER Ret = NULL;
    DWORD* p;

    ENTRY("PAL_MakeOptimizedTlsGetter(dwTlsIndex=%u)\n", dwTlsIndex);

#define TLS_OPTIMIZED_GETTER_SIZE 37

    p = (DWORD*)malloc(TLS_OPTIMIZED_GETTER_SIZE * sizeof(DWORD));

    if (p == NULL) {
        goto done;
    }

#define _HI(x) (((size_t)(x)) >> 16)
#define _LO(x) ((UINT16)(size_t)(x))

#define _HA(x) (((1<<15) + ((size_t)(x))) >> 16)
#define _LA(x) ((UINT16)(((size_t)(x)) - (_HA(x) << 16)))

    p[ 0] = 0x7c298e70; // srawi r9,r1,17 // key = sp >> 17;
    p[ 1] = 0x7c28c670; // srawi r8,r1,24 // key -= key >> 7;
    p[ 2] = 0x7ce84850; // subf r7,r8,r9
    p[ 3] = 0x7ce62e70; // srawi r6,r7,5 // key -= key >> 5;
    p[ 4] = 0x7d263850; // subf r9,r6,r7
    p[ 5] = 0x7d281e70; // srawi r8,r9,3 // key -= key >> 3;
    p[ 6] = 0x7ce84850; // subf r7,r8,r9
    p[ 7] = 0x54e615ba; // rlwinm r6,r7,2,22,29 // x &= 0xFF;
    p[ 8] = 0x3ca00000 + _HA(&flush_counter); // addis r5,0,ha(flush_counter)
    p[ 9] = 0x3c860000 + _HA(&thread_hints); // addis r4,r6,ha(thread_hints)
    p[10] = 0x81250000 + _LA(&flush_counter); // lwz r9,la(flush_counter)(r5) // counter = flush_counter;
    p[11] = 0x80640000 + _LA(&thread_hints); // lwz r3,la(thread_hints)(r4) // THREAD *thread = thread_hints[x];
    p[12] = 0x80c30000 + offsetof(THREAD, minStack); // lwz r6,minStack(r3)
    p[13] = 0x80e30000 + offsetof(THREAD, maxStack); // lwz r7,maxStack(r3)
    p[14] = 0x7c060800; // cmpw cr0,r6,r1 // if ((size_t)pThread->minStack <= sp && sp < (size_t)pThread->maxStack)
    p[15] = 0x7c813800; // cmpw cr1,r1,r7
    p[16] = 0x4181001c; // bgt- cr0,L2
    p[17] = 0x40840018; // bge- cr1,L2
    p[18] = 0x81050000 + _LA(&flush_counter); // lwz r8,la(flush_counter)(r5) // if (counter == flush_counter)
    p[19] = 0x7c084800; // cmpw cr0,r8,r9
    p[20] = 0x4082000c; // bne- cr0,L2
    p[21] = (dwTlsIndex != THREAD_OBJECT_TLS_INDEX) ?
        (0x80630000 + offsetof(THREAD, tlsSlots[dwTlsIndex])) : // lwz r3,(slot+4*index)(r3)
        0x4e800020; // blr
    p[22] = 0x4e800020; // blr
// L2:
    p[23] = 0x7c4802a6; // mflr r2
    p[24] = 0x90410008; // stw r2,8(r1)
    p[25] = 0x9421ffb0; // stwu r1,-80(r1)
    p[26] = 0x7c230b78; // mr r3, r1 // arg1 = sp
    p[27] = 0x38840000 + _LA(&thread_hints); // addi r4, r4, la(thread_hints) // arg2 = ppThreadHint
    p[28] = 0x3c000000 + _HI(&PROCGetCurrentThreadObjectSlow); // lis r0,hi(PROCGetCurrentThreadObjectSlow)
    p[29] = 0x600c0000 + _LO(&PROCGetCurrentThreadObjectSlow); // ori r12,r0,lo(PROCGetCurrentThreadObjectSlow)
    p[30] = 0x7d8903a6; // mtctr r12
    p[31] = 0x4e800421; // bctrl
    p[32] = 0x80410058; // lwz r2,88(r1)
    p[33] = 0x38210050; // addi r1,r1,80
    p[34] = 0x7c4803a6; // mtlr r2
    p[35] = (dwTlsIndex != THREAD_OBJECT_TLS_INDEX) ?
        (0x80630000 + offsetof(THREAD, tlsSlots[dwTlsIndex])) : // lwz r3,(slot+4*index)(r3)
        0x4e800020; // nop
    p[36] = 0x4e800020; // blr

#undef _HI
#undef _LO

#undef _HA
#undef _LA

    DBG_FlushInstructionCache(p, TLS_OPTIMIZED_GETTER_SIZE * sizeof(DWORD));

    Ret = (PAL_POPTIMIZEDTLSGETTER)p;

done:
    LOGEXIT("PAL_MakeOptimizedTlsGetter returns PAL_POPTIMIZEDTLSGETTER %p\n", Ret);

    return Ret;
}

/*++
Function:
    TLSFreeOptimizedGetter

    Frees a function created by MakeOptimizedTlsGetter().
--*/
VOID
TLSFreeOptimizedGetter(
        IN PAL_POPTIMIZEDTLSGETTER pOptimizedTlsGetter)
{
    free(pOptimizedTlsGetter);
}

#endif // USE_OPTIMIZEDTLSGETTER
